/*
 * @Descripttion: HsPwdEncrypt.h 的实现
 * @Author: yubo
 * @Date: 2022-10-12 09:43:46
 * @LastEditTime: 2022-10-12 10:28:56
 */
#include "HsPwdEncrypt.h"

#define XDIGIT_TO_NUM(h) ((h) < 'a' ? (h) - '0' : tolower(h) - 'a' + 10)
#define X2DIGITS_TO_NUM(h1, h2) ((XDIGIT_TO_NUM(h1) << 4) + XDIGIT_TO_NUM(h2))

namespace HST3
{
    // base64转码
    std::string PwdEncrypt::Base64Encode(const char *buffer, int length, bool newLine)
    {
        BIO *bmem = NULL;
        BIO *b64 = NULL;
        BUF_MEM *bptr;

        b64 = BIO_new(BIO_f_base64());
        if (!newLine)
        {
            BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
        }
        bmem = BIO_new(BIO_s_mem());
        b64 = BIO_push(b64, bmem);
        BIO_write(b64, buffer, length);
        BIO_flush(b64);
        BIO_get_mem_ptr(b64, &bptr);
        BIO_set_close(b64, BIO_NOCLOSE);

        std::string result;
        result.assign(bptr->data, bptr->length);

        BIO_free_all(b64);

        return result;
    }

    // base64 解码
    std::string PwdEncrypt::Base64Decode(const char *input, int length, bool newLine)
    {
        BIO *b64 = NULL;
        BIO *bmem = NULL;
        b64 = BIO_new(BIO_f_base64());
        if (!newLine)
        {
            BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
        }
        bmem = BIO_new_mem_buf((void *)input, length);
        b64 = BIO_push(b64, bmem);
        std::string result;
        result.resize(length);

        int size = BIO_read(b64, (void *)result.data(), length);
        result.resize(size);

        BIO_free_all(b64);

        return result;
    }

    // AES加密pkcs#5填充
    bool PwdEncrypt::AESEncrypt(const char *pszKey, const char *buffer, size_t length, std::string *res)
    {
        unsigned char keyBuf[AES_KEY_LENGTH];
        unsigned char iv[AES_KEY_LENGTH] = {10, 4, 117, 110, 100, 11, 5, 11, 7, 36, 98, 25, 15, 11, 7, 10};
        // unsigned char iv[AES_KEY_LENGTH] = { 0 };

        AES_KEY aesKey;
        int ret;

        unsigned int uiPaddingLen;
        unsigned int uiTotalLen;
        unsigned char *pData;

        memcpy(keyBuf, pszKey, AES_KEY_LENGTH);

        ret = AES_set_encrypt_key(keyBuf, 128, &aesKey);
        if (ret != 0)
            return false;

        uiPaddingLen = AES_KEY_LENGTH - (length % AES_KEY_LENGTH);
        uiTotalLen = length + uiPaddingLen;
        pData = (unsigned char *)malloc(uiTotalLen);
        memcpy(pData, buffer, length);

        if (uiPaddingLen > 0)
            memset(pData + length, uiPaddingLen, uiPaddingLen);

        res->clear();
        res->resize(uiTotalLen);

        AES_cbc_encrypt(pData, (unsigned char *)res->data(), uiTotalLen, &aesKey, iv, AES_ENCRYPT);

        free(pData);
        return true;
    }

    bool PwdEncrypt::AESEncrypt_2(unsigned char cKeyArr[16], const char *buffer, size_t length, std::string *res)
    {
        unsigned char keyBuf[AES_KEY_LENGTH];
        unsigned char iv[AES_KEY_LENGTH] = {10, 4, 117, 110, 100, 11, 5, 11, 7, 36, 98, 25, 15, 11, 7, 10};

        AES_KEY aesKey;
        int ret;

        unsigned int uiPaddingLen;
        unsigned int uiTotalLen;
        unsigned char *pData;

        memcpy(keyBuf, cKeyArr, AES_KEY_LENGTH);

        ret = AES_set_encrypt_key(keyBuf, 128, &aesKey);
        if (ret != 0)
            return false;

        uiPaddingLen = AES_KEY_LENGTH - (length % AES_KEY_LENGTH);
        uiTotalLen = length + uiPaddingLen;
        pData = (unsigned char *)malloc(uiTotalLen);
        memcpy(pData, buffer, length);

        if (uiPaddingLen > 0)
            memset(pData + length, uiPaddingLen, uiPaddingLen);

        res->clear();
        res->resize(uiTotalLen);

        AES_cbc_encrypt(pData, (unsigned char *)res->data(), uiTotalLen, &aesKey, iv, AES_ENCRYPT);

        free(pData);
        return true;
    }

    // AES解密pkcs#5填充
    bool PwdEncrypt::AESDecrypt(const char *pszKey, const char *buffer, size_t length, std::string *res)
    {
        unsigned char keyBuf[AES_KEY_LENGTH];
        unsigned char iv[AES_KEY_LENGTH] = {10, 4, 117, 110, 100, 11, 5, 11, 7, 36, 98, 25, 15, 11, 7, 10};

        AES_KEY aesKey;
        int ret = 0;
        int uiPaddingLen = 0;
        memcpy(keyBuf, pszKey, AES_KEY_LENGTH);

        ret = AES_set_decrypt_key(keyBuf, 128, &aesKey);
        if (ret != 0)
            return false;

        res->clear();
        res->resize(length);

        AES_cbc_encrypt((unsigned char *)buffer, (unsigned char *)res->data(), length, &aesKey, iv, AES_DECRYPT);

        uiPaddingLen = (*res)[length - 1];
        if (uiPaddingLen > AES_KEY_LENGTH || uiPaddingLen < 0)
        {
            return false;
        }
        res->resize(length - uiPaddingLen);

        return true;
    }

    // 解密函数入口
    bool PwdEncrypt::Decode(const char *pszKey, const char *buffer, size_t length, std::string *res)
    {
        std::string hex = Base64Decode(buffer, length);
        return AESDecrypt(pszKey, hex.c_str(), hex.length(), res);
    }

    // 加密函数入口
    bool PwdEncrypt::Encode(const char *pszKey, const char *buffer, size_t length, std::string *res)
    {
        std::string temp_res;
        bool ret = AESEncrypt(pszKey, buffer, length, &temp_res);
        res->assign(Base64Encode(temp_res.c_str(), temp_res.length()));
        return ret;
    }

    bool PwdEncrypt::Encode_2(unsigned char cKeyArr[16], const char *buffer, size_t length, std::string *res)
    {
        std::string temp_res;
        bool ret = AESEncrypt_2(cKeyArr, buffer, length, &temp_res);
        res->assign(Base64Encode(temp_res.c_str(), temp_res.length()));
        return ret;
    }

    std::string PwdEncrypt::MD5(const std::string &src)
    {
        MD5_CTX ctx;

        std::string md5_string;
        unsigned char md[16] = {0};
        char tmp[33] = {0};

        MD5_Init(&ctx);
        MD5_Update(&ctx, src.c_str(), src.size());
        MD5_Final(md, &ctx);

        for (int i = 0; i < 16; ++i)
        {
            memset(tmp, 0x00, sizeof(tmp));
            sprintf(tmp, "%02x", md[i]);
            md5_string += tmp;
        }
        return md5_string;
    }

    void PwdEncrypt::MD5_2(const std::string &src, unsigned char *md)
    {
        MD5_CTX ctx;

        MD5_Init(&ctx);
        MD5_Update(&ctx, src.c_str(), src.size());
        MD5_Final(md, &ctx);
    }

    std::string PwdEncrypt::StrToHex(const unsigned char *p, int iLen)
    {
        char psz[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
                      'b', 'c', 'd', 'e', 'f'};
        char *pBuf = new char[iLen * 2 + 1];
        for (int i = 0; i < iLen; ++i)
        {
            pBuf[i * 2] = psz[(p[i] & 0xF0) >> 4];
            pBuf[i * 2 + 1] = psz[p[i] & 0xF];
        }
        pBuf[iLen * 2] = '\0';
        std::string ret = pBuf;
        delete[] pBuf;
        return ret;
    }

    void PwdEncrypt::HexToStr(const unsigned char *pHex, unsigned int nHexLen,
                         unsigned char *pBuf, unsigned int &nBufLen)
    {
        if ((NULL == pHex) || (0 == nHexLen))
            return;

        unsigned int nReadPos = 0;
        unsigned int nWritePos = 0;
        if (0 != (nHexLen % 2))
        {
            if (isxdigit((unsigned char)pHex[nReadPos]))
            {
                pBuf[nWritePos++] = X2DIGITS_TO_NUM('0', pHex[nReadPos]);
            }
            nReadPos++;
        }
        while (nReadPos < nHexLen)
        {
            pBuf[nWritePos++] = X2DIGITS_TO_NUM(pHex[nReadPos],
                                                pHex[nReadPos + 1]);
            nReadPos += 2;
        }
        nBufLen = nWritePos;
    }

}